// $Id: translate.cpp 2 2009-09-02 08:47:42Z joseph2002 $
/*
 * Copyright 2009 Joseph Wu
 * Distributed under the Boost Software License, Version 1.0.
 * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
 *
 * For more information, see http://xpcomplus.sourceforge.net
 */

#include <new>
#include <stdexcept>
#include <boost/mpl/list.hpp>
#include <boost/test/unit_test.hpp>
#include <boost/test/test_case_template.hpp>
#include <boost/type_traits/integral_constant.hpp>
#include <xpcom/exception.hpp>
#include <xpcom/result_code.hpp>
#include <xpcom/impl/exception.hpp>

namespace {

typedef boost::mpl::list<
    boost::integral_constant<xpcom::result_t, NS_ERROR_NULL_POINTER>,
    boost::integral_constant<xpcom::result_t, NS_ERROR_NO_INTERFACE>,
    boost::integral_constant<xpcom::result_t, NS_ERROR_OUT_OF_MEMORY>,
    boost::integral_constant<xpcom::result_t, NS_ERROR_INVALID_ARG>,
    boost::integral_constant<xpcom::result_t, NS_ERROR_FAILURE>
> test_xpcom_exception_types;

void func_impl_ok()
{
}

xpcom::result_t func_ok()
{
    XPCOM_IMPL_METHOD_BEGIN
    {
        func_impl_ok();
    }
    XPCOM_IMPL_METHOD_END

    return NS_OK;
}

template <xpcom::result_t Val>
void func_impl_xpcom_exception()
{
    throw xpcom::exception(Val);
}

template <xpcom::result_t Val>
xpcom::result_t func_xpcom_exception()
{
    XPCOM_IMPL_METHOD_BEGIN
    {
        func_impl_xpcom_exception<Val>();
    }
    XPCOM_IMPL_METHOD_END

    return NS_OK;
}

void func_impl_bad_alloc()
{
    throw std::bad_alloc();
}

xpcom::result_t func_bad_alloc()
{
    XPCOM_IMPL_METHOD_BEGIN
    {
        func_impl_bad_alloc();
    }
    XPCOM_IMPL_METHOD_END

    return NS_OK;
}

void func_impl_exception()
{
    throw std::runtime_error("aaa");
}

xpcom::result_t func_exception()
{
    XPCOM_IMPL_METHOD_BEGIN
    {
        func_impl_exception();
    }
    XPCOM_IMPL_METHOD_END

    return NS_OK;
}

}

BOOST_AUTO_TEST_CASE(test_translate_ok)
{
    using namespace xpcom;

    try {
        result_t r = func_ok();
        BOOST_CHECK(true);
        BOOST_CHECK(r == NS_OK);

    } catch (...) {
        BOOST_CHECK(false);
    }
}

BOOST_AUTO_TEST_CASE_TEMPLATE(test_translate_xpcom_exception, T, test_xpcom_exception_types)
{
    using namespace xpcom;

#ifndef XPCOM_DISABLE_METHOD_EXCEPTION_TRANSLATION
    try {
        result_t r = func_xpcom_exception<T::value>();
        BOOST_CHECK(true);
        BOOST_CHECK(r == T::value);

    } catch (...) {
        BOOST_CHECK(false);
    }

#else // XPCOM_DISABLE_METHOD_EXCEPTION_TRANSLATION
    try {
        func_xpcom_exception<T::value>();
        BOOST_CHECK(false);

    } catch (const xpcom::exception& e) {
        BOOST_CHECK(true);
        BOOST_CHECK(e.code() == T::value);

    } catch (...) {
        BOOST_CHECK(false);
    }

#endif // XPCOM_DISABLE_METHOD_EXCEPTION_TRANSLATION
}

BOOST_AUTO_TEST_CASE(test_translate_bad_alloc)
{
    using namespace xpcom;

#ifndef XPCOM_DISABLE_METHOD_EXCEPTION_TRANSLATION
    try {
        result_t r = func_bad_alloc();
        BOOST_CHECK(true);
        BOOST_CHECK(r == NS_ERROR_OUT_OF_MEMORY);

    } catch (...) {
        BOOST_CHECK(false);
    }

#else // XPCOM_DISABLE_METHOD_EXCEPTION_TRANSLATION
    try {
        func_bad_alloc();
        BOOST_CHECK(false);

    } catch (const std::bad_alloc&) {
        BOOST_CHECK(true);

    } catch (...) {
        BOOST_CHECK(false);
    }

#endif // XPCOM_DISABLE_METHOD_EXCEPTION_TRANSLATION
}

BOOST_AUTO_TEST_CASE(test_translate_exception)
{
    using namespace xpcom;

#ifndef XPCOM_DISABLE_METHOD_EXCEPTION_TRANSLATION
    try {
        result_t r = func_exception();
        BOOST_CHECK(true);
        BOOST_CHECK(r == NS_ERROR_FAILURE);

    } catch (...) {
        BOOST_CHECK(false);
    }

#else // XPCOM_DISABLE_METHOD_EXCEPTION_TRANSLATION
    try {
        func_exception();
        BOOST_CHECK(false);

    } catch (const std::exception&) {
        BOOST_CHECK(true);

    } catch (...) {
        BOOST_CHECK(false);
    }

#endif // XPCOM_DISABLE_METHOD_EXCEPTION_TRANSLATION
}
